﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Markup;

using agree.configuration;
using agree.Pmrs;
using agree.schema;

using alib;
using alib.Collections.ReadOnly;
using alib.Debugging;
using alib.Enumerable;
using alib.Reflection;

namespace agree
{

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class GenerationInputMaker : m_scalar_base<Tfs, GenerationInput>,
		ρρMrsManager
	{
		public GenerationInputMaker(IRtParent parent, Tfs tfs)
			: base(parent, tfs)
		{
		}

		protected override GenerationInput start(Tfs t)
		{
			return new GenerationInput(this, t);
		}

		MrsManager mrsm;
		public MrsManager MrsManager
		{
			get { return this.mrsm; }
			set { this.mrsm = value; }
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class ParseOutputToGenerationInput : m_scalar_base<IDerivation, GenerationInput>,
		ρρGrammar
	{
		public ParseOutputToGenerationInput(IRtParent parent, IDerivation deriv)
			: base(parent, deriv)
		{
		}

		protected override GenerationInput start(IDerivation deriv)
		{
			PortableMrs pmrs = new PortableMrs(this, deriv.DerivationTfs());

			Tfs tfs = pmrs.Write(g.fsu, g.ftm.r_none, default(Dictionary<String, String>), Tfs.Flags.Skolemized | Tfs.Flags.WantVarbits);

			return new GenerationInput(this, tfs);
		}


		///////////////////////////////////////////////////////////////////////
		///
		Grammar g;
		public Grammar Grammar
		{
			get { return g; }
			set { g = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class GenerationInput : identity_node
	{
		public GenerationInput(IRtParent parent, ITfsSlot mrs_in)
			: base(parent, mrs_in)
		{
			this.ts_mrs = new TfsSlot(mrs_in);

			this.mrsm = ts_mrs.tfs.mrsm;

			Debug.Assert((ts_mrs.tfs.flags & Tfs.Flags.Skolemized) != 0);

			this.RelSlots = mrsm.GetRelSlots(ts_mrs);

			int c = RelSlots.Length;
			this.RelKeys = new LexLookupKey[c];
			this.RelVarbits = new ulong[c];
			this.AllRelsMask = ((ulong)1 << c) - 1;
			this.AllVarBits = 0;

			for (int i = 0; i < c; i++)
			{
				TfsSlot ts_rel = RelSlots[i];

				RelKeys[i] = new LexLookupKey(ts_rel);

				AllVarBits |= (RelVarbits[i] = ts_rel.FindAccessibleIndices());
			}

			this.NoCoverage = new CoverageMap(this);
		}

		public readonly MrsManager mrsm;

		public readonly TfsSlot ts_mrs;
		public readonly TfsSlot[] RelSlots;
		public readonly LexLookupKey[] RelKeys;
		public readonly ulong[] RelVarbits;
		public readonly ulong AllRelsMask;
		public readonly ulong AllVarBits;
		public readonly CoverageMap NoCoverage;

		public MrsManager MrsManager
		{
			get { return mrsm; }
			set { throw not.valid; }
		}

		public RelMatchHelper RelMatchHelper
		{
			get { return mrsm.RelMatchHelper; }
			set { throw not.valid; }
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// add semantically vacuous lexical entries according to trigger rules
	/// TriggerRuleGroups is an index of TriggerRules by LexicalEntry that it introduces, so this
	/// minimizes rule comparisons and prevents duplicate introduction of vacuous EPs
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class ApplyTriggerRules : m_observable<GenerationInput, IChartObj<CoverageMap>>,
		ρρTriggerRuleMgr
	{
		public ApplyTriggerRules(IRtParent parent, GenerationInput input)
			: base(parent, input)
		{
		}

		///////////////////////////////////////////////////////////////////////
		///
		TriggerRuleMgr trm;
		public TriggerRuleMgr TriggerRuleMgr
		{
			get { return trm; }
			set { trm = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////

		protected override void start(GenerationInput input)
		{
			int c = 0;
			foreach (var vacuous_le in trm.CheckRules(input))
			{
				_item(vacuous_le);
				c++;
			}

			Debug.Print(String.Format("found {0} vacuous lexical entries from trigger rules", c));

			_remove_task();
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// determine and skolemize the set of rules that aren't summarily excluded
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class DetermineRelevantRules : m_scalar_base<GenerationInput, GenerationInput>,
		ρρGeneratorActiveRules,
		ρρRelMatchHelper,
		ρρRuleSet
	{
		public DetermineRelevantRules(IRtParent parent, GenerationInput input)
			: base(parent, input)
		{
		}

		///////////////////////////////////////////////////////////////////////
		///
		RelMatchHelper rmh;
		public RelMatchHelper RelMatchHelper
		{
			get { return rmh; }
			set { rmh = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		Rule[] source;
		public Rule[] RuleSet
		{
			get { return source; }
			set { source = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		IActiveRule[] output;
		public IActiveRule[] GeneratorActiveRules
		{
			get { return output; }
			set { throw not.valid; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		protected override GenerationInput start(GenerationInput input)
		{
			var rules_list = new List<IActiveRule>();

			var input_rels = input.RelSlots;
			var mrsm = input.mrsm;

			foreach (Rule rule in source)
			{
				TfsSlot[] rel_slots = mrsm.GetRelSlots(rule.CContSlot);

				if (rel_slots == null || rel_slots.Length == 0)
				{
					// don't need to skolemize rules without C-CONT
					rules_list.Add(rule);

					//bp("vacuous rule", grs);
					//Debug.Print("ok: vacuous rule {0}", grs);
				}
				else if (rel_slots.All(rule_rel => input_rels.Any(inp_rel => mrsm.ComparePred(rule_rel, inp_rel))))
				{
					foreach (var grs in rule.SkolemizeFor(input))
					{
						rules_list.Add(grs);

						//bp("skolemized rule", grs);
						//Debug.Print("ok: skolemized rule {0}", grs);
					}
				}
				else
				{
					/// (rule has extraneous content)
				}
			}
			this.output = rules_list.ToArray();

			Debug.Print("found {0} relevant rules for generation", output.Length);

			input.ForwardTrace(this);

			return input;
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// control harness for a generation operation
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DisplayName("Generation Control Instance")]
	public unsafe sealed partial class GenControl : ChartControl<GenControl, CoverageMap>
	{
		public GenControl(IRtParent parent, IActiveObj<IChartObj<CoverageMap>> items)
			: base(parent, items)
		{
		}

		ChartCell[] chart;

		public GenerationInput input;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Return a (task which promises a) generation chart containing all surface realization which are compatible with 
		/// the specified input MRS.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void Initialize()
		{
			this.input = this.FindTrace<GenerationInput>();

			//_debug_show_gen_input(input_mrs);
			//bp("Start Generate", input_mrs.ts_mrs.tfs);
			//bp("MRS", input_mrs);

			if ((base.spanning_count = input.RelSlots.Length) <= 0)
				return;

			if ((rules = rules ?? this.FindTrace<DetermineRelevantRules>().GeneratorActiveRules) == null || rules.Length == 0)
				return;

			if ((rgss = rgss ?? tu.em.GeneratorStartSymbols) == null || rgss.Length == 0)
				return;

			if (pack_opt != 0)
				this.unify_opts |= Tfs.Flags.WantVarbits;

			this.rr = u.ftm.GenerationRestrictors;

			int c = spanning_count + 1;
			this.chart = new ChartCell[c];
			this.rgns = new NotificationSource[c];
			for (int i = 0; i < c; i++)
			{
				chart[i] = new ChartCell();
				rgns[i] = new NotificationSource(this, true);
			}
			base.spanning_cell = chart[spanning_count];
		}

		[Conditional("STATS")]
		public void IndexAccessibilityRejection()
		{
			//Interlocked.Increment(ref stats.Generation.index_accessibility_rejections);
		}
	};
}
